using UnityEngine;
using System.Collections;

[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
public class ConeVision : MonoBehaviour
{
    #region Attributes

    public float minDistanceToDrawCone = 10;

    public int numbersOfRays = 500;
    public float curvatureFactor = 0;
    private float viewRange = 11;
    private float viewAngle = 120;

    private Mesh mesh;
    private int numVertex;
    private int numTriagles;
    private Vector3[] vertexBuffer;
    private int[] indexBuffer;
    private Vector2[] uv;
    private Vector3[] normals;

    private PlayerControl player;

    #endregion

    #region Methods

    public Vector3 PlayerDistance
    {
        get
        {
            return this.player.transform.position - this.transform.position;
        }
    }

    private void InitMesh()
    {
        MeshFilter meshFilter = this.GetComponent<MeshFilter>();
        this.mesh = new Mesh();
        this.mesh.name = TextConstants.CONE_VISION_NAME;

        this.numVertex = this.numbersOfRays + 1;
        this.numTriagles = this.numbersOfRays - 1;

        this.vertexBuffer = new Vector3[this.numVertex];
        this.indexBuffer = new int[this.numTriagles * 3];
        this.uv = new Vector2[this.numVertex];
        this.normals = new Vector3[this.numVertex];

        meshFilter.mesh = this.mesh;

        this.player = GameControl.FindPlayer();
    }

    private void CreateIndexBuffer()
    {
        for (int i = 0; i < this.numTriagles; i++)
        {
            this.indexBuffer[i * 3] = 0;
            this.indexBuffer[i * 3 + 1] = i + 1;
            this.indexBuffer[i * 3 + 2] = i + 2;
        }
    }

    private void CreateVertexBuffer()
    {
        float currentAngle = this.viewAngle / 2;
        float angleOffset = this.viewAngle / this.numTriagles;

        this.AddVertex(0, Vector3.zero, 0);
     
        for (int i = 1; i < this.numVertex; i++)
        {
            Vector3 direction = Quaternion.Euler(0, currentAngle, 0) * this.transform.forward;

            int layerMask = TagConstants.LAYER_OBSTACLES;
            RaycastHit hit;
            if (Physics.Raycast(this.transform.position, direction, out hit, this.viewRange, layerMask))
            {
                this.AddVertex(i, this.transform.InverseTransformPoint(hit.point), currentAngle);
            }
            else
            {
                this.AddVertex(i, this.transform.InverseTransformDirection(direction) * this.viewRange, currentAngle);
            }

            currentAngle -= angleOffset;
        }
    }

    private void AddVertex(int i, Vector3 point, float currentAngle)
    {
        // cosine return value between 0 and 1
        point.y = Mathf.Cos(Mathf.Deg2Rad * currentAngle) * this.curvatureFactor - this.curvatureFactor;
        this.vertexBuffer[i] = point;
        this.normals[i] = Vector3.up;
        this.uv[i] = new Vector2(point.z, point.x);
    }

    #endregion

    #region Unity Methods

    void Start() 
    {
        EnemyControl control = this.transform.parent.GetComponent<EnemyControl>();
        if (control != null)
        {
            this.viewRange = control.viewRange;
            this.viewAngle = control.viewAngle;
        }
        else
            Debug.LogWarning(TextConstants.ENEMY_NOT_FOUND_ERROR);

        this.InitMesh();
	}
	
	void LateUpdate() 
    {
        if (this.PlayerDistance.magnitude <= this.minDistanceToDrawCone)
        {
            this.CreateVertexBuffer();
            this.CreateIndexBuffer();

            this.mesh.vertices = this.vertexBuffer;
            this.mesh.triangles = this.indexBuffer;
            this.mesh.uv = uv;
            this.mesh.normals = this.normals;
        }
	}

    #endregion
}
